﻿Namespace Shows
	Public Class Archiving
		Public Class Result
			Public Property SaveResult As Boolean = False
			Public Property UnusedFileList As New List(Of String)
			Public Property MissingFileList As New List(Of String)
		End Class

        ''' <summary>
        ''' Run the commands to create the archive.
        ''' </summary>
        ''' <remarks></remarks>
        Public Shared Function CreateOrUpdateArchive(FilePath As String, Optional DeleteUnusedAudioFiles As Boolean = False) As Result
            Dim SaveResult As New Result()

            Try
                If FileIO.FileSystem.FileExists(FilePath) Then
                    If FileIO.FileSystem.FileExists(FilePath & ".temp") Then FileIO.FileSystem.DeleteFile(FilePath & ".temp")
                    FileIO.FileSystem.CopyFile(FilePath, FilePath & ".temp")
                End If
                Using pack As System.IO.Packaging.ZipPackage = System.IO.Packaging.ZipPackage.Open(FilePath & ".temp", IO.FileMode.OpenOrCreate)

                    If pack.GetParts.Count > 0 Then
                        Dim AudioPartList = pack.GetParts().Where(Function(part) part.Uri.OriginalString.StartsWith("/AudioFiles")).ToList
                        For Each p In AudioPartList
                            If DeleteUnusedAudioFiles Then
                                Dim result = (From i In My.Application.Show.ShowFile.CueList.GetAudioCues Where CType(i, Cues.Audio).FileName = Uri.UnescapeDataString(p.Uri.OriginalString).Remove(0, 12).Replace("/", "\")).Count
                                If result < 1 Then
                                    pack.DeletePart(p.Uri)
                                End If
                            ElseIf Not DeleteUnusedAudioFiles Then
                                Dim temp As String = Uri.UnescapeDataString(p.Uri.OriginalString)
                                Dim temp2 As String = temp.Remove(0, 12)
                                Dim temp3 As String = temp2.Replace("/", "\")
                                Dim result = (From i In My.Application.Show.ShowFile.CueList.GetAudioCues Where CType(i, Cues.Audio).FileName = temp3).Count
                                If result < 1 Then
                                    SaveResult.UnusedFileList.Add(temp3)
                                End If
                            End If
                        Next
                    End If
                    For Each a As Cues.Audio In My.Application.Show.ShowFile.CueList.GetAudioCues
                        If pack.GetParts.Where(Function(p)
                                                   Dim temp As String = Uri.UnescapeDataString(p.Uri.OriginalString).Replace("/", "\")
                                                   Return temp.Contains(a.FileName)
                                               End Function)?.Count < 1 Then

                            If FileIO.FileSystem.FileExists(a.FileName) Then
                                CreateAudioFilePart(pack, a.FileName)
                            Else
                                SaveResult.MissingFileList.Add(a.FileName)
                            End If

                        End If
                    Next
                    If SaveResult.MissingFileList.Count > 0 Then
                        If MsgBox($"There {If(SaveResult.MissingFileList.Count = 1, $"was {SaveResult.MissingFileList.Count} audio file", $"were {SaveResult.MissingFileList.Count} audio files ")} that could not be found in the archive. Do you want to locate {If(SaveResult.MissingFileList.Count = 1, "it", "them")} now?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
                            For Each f In SaveResult.MissingFileList.Distinct
                                Dim browser As New Microsoft.Win32.OpenFileDialog()
                                browser.CheckFileExists = True
                                browser.CheckPathExists = True
                                browser.Filter = "Compatible Audio Files|*.aif;*.aiff;*.mp3;*.wav;*.wma"    '"All Audio Files|*.mp3;*.wav;*.aif;*.aiff;*.ogg;*.bwav;*.wma"
                                browser.Title = "Looking for audio file: " & FileIO.FileSystem.GetName(f)
                                browser.Multiselect = False
                                browser.InitialDirectory = FileIO.FileSystem.GetFileInfo(f).DirectoryName

                                If browser.ShowDialog() = True Then
                                    Try
                                        pack.DeletePart(GetAudioUri(f))
                                        CreateAudioFilePart(pack, browser.FileName)
                                        For Each c As Cues.Audio In My.Application.Show.ShowFile.CueList.OfType(Of Cues.Audio).Where(Function(cue) cue.FileName = f)
                                            c.FileName = browser.FileName
                                        Next
                                    Catch ex As Exception
                                        My.Application.Show.Console.SendMessage("There was a problem locating a missing audio file.", ex)
                                    End Try
                                End If
                            Next
                        End If
                    End If
                    If SaveResult.UnusedFileList.Count > 0 Then
                        If MsgBox("There are " & SaveResult.UnusedFileList.Count & " audio files in the archive that are not referenced by any cues. Do you want to delete them?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
                            For Each f In SaveResult.UnusedFileList
                                DeleteAudioFile(pack, f)
                            Next
                        End If
                    End If

                    For Each p In pack.GetParts().Where(Function(part) part.Uri.OriginalString.StartsWith("/ShowFile")).ToList
                        pack.DeletePart(p.Uri)
                    Next
                    CreateShowFilePart(pack)

                End Using
                If FileIO.FileSystem.FileExists(FilePath & ".bkup") Then
                    FileIO.FileSystem.DeleteFile(FilePath & ".bkup")
                End If
                If FileIO.FileSystem.FileExists(FilePath) Then
                    FileIO.FileSystem.RenameFile(FilePath, FileIO.FileSystem.GetName(FilePath) & ".bkup")
                End If
                FileIO.FileSystem.RenameFile(FilePath & ".temp", FileIO.FileSystem.GetName(FilePath))
                If Not My.Settings.KeepBackupPackage AndAlso FileIO.FileSystem.FileExists(FilePath & ".bkup") Then
                    FileIO.FileSystem.DeleteFile($"{FilePath}.bkup")
                End If
                SaveResult.SaveResult = True


            Catch ex As Exception
                My.Application.Show.Console.SendMessage("ERROR: Couldn't create the  cue archive. Please try again!", ex)
                MsgBox("ERROR: Couldn't create the cue archive. Please try again!", MsgBoxStyle.Critical)
                If IO.File.Exists(FilePath) AndAlso FileIO.FileSystem.GetFileInfo(FilePath).Length = 0 Then
                    IO.File.Delete(FilePath)
                End If
                If IO.File.Exists(FilePath & ".temp") Then FileIO.FileSystem.DeleteFile(FilePath & ".temp")
                SaveResult.SaveResult = False
            End Try
            Return SaveResult
        End Function

        ''' <summary>
        ''' Deletes an audio file from the provided pack file.
        ''' </summary>
        ''' <param name="Pack">The pack to delete the file from.</param>
        ''' <param name="FilePath">The original file path of the audio cue to delete. The URI for the part in the pack is created automatically.</param>
        Public Shared Sub DeleteAudioFile(Pack As System.IO.Packaging.ZipPackage, FilePath As String)
			Dim uri As Uri = System.IO.Packaging.PackUriHelper.CreatePartUri(New Uri("AudioFiles\" & FilePath, UriKind.Relative))
			Pack.DeletePart(uri)
		End Sub

		''' <summary>
		''' Creates a URI for AudioFiles
		''' </summary>
		''' <param name="FilePath">The FilePath to create the URI with.</param>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Shared Function GetAudioUri(FilePath As String) As Uri
			Return System.IO.Packaging.PackUriHelper.CreatePartUri(New Uri("AudioFiles\" & FilePath, UriKind.Relative))
		End Function

        ''' <summary>
        ''' Gets a pack file referencing the current show file's path.
        ''' </summary>
        ''' <returns></returns>
        Public Shared Function GetCurrentShowPack(Optional AsTemp As Boolean = False) As System.IO.Packaging.ZipPackage
            If My.Application.Show.ShowFile.IsPackage Then
                Return IO.Packaging.ZipPackage.Open($"{My.Application.Show.ShowFile.FullPath?.ToString}{If(AsTemp, ".temp", "")}", If(AsTemp, IO.FileMode.OpenOrCreate, IO.FileMode.Open))
            Else
                Return Nothing
            End If
        End Function

        ''' <summary>
        ''' Uses the file paths of the audio files in the show to refresh the audio files in the archive.
        ''' Returns the number of files that were updated.
        ''' </summary>
        ''' <remarks></remarks>
        Public Shared Function RefreshAudioFiles() As Integer 'pack As System.IO.Packaging.ZipPackage, Show As ShowFile, Optional LoadTimes As Boolean = True) As Integer
            Dim UpdatedCount As Integer = 0
            Using pack As System.IO.Packaging.ZipPackage = GetCurrentShowPack()


                Dim mfl As New List(Of Cues.Audio)
                For Each ac In My.Application.Show.ShowFile.CueList.GetAudioCues.Where(Function(c) c.FileName <> "")
                    Dim uri As Uri = GetAudioUri(ac.FileName)
                    If Not pack.PartExists(uri) Then
                        mfl.Add(ac)
                    End If
                Next


                If mfl.Count > 0 Then
                    If MsgBox($"There {If(mfl.Count = 1, $"was {mfl.Count} audio file", $"were {mfl.Count} audio files ")} that could not be found in the archive. Do you want to locate {If(mfl.Count = 1, "it", "them")} now?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
                        For Each c In mfl
                            Dim browser As New Microsoft.Win32.OpenFileDialog()
                            browser.CheckFileExists = True
                            browser.CheckPathExists = True
                            browser.Filter = "Compatible Audio Files|*.aif;*.aiff;*.mp3;*.wav;*.wma"    '"All Audio Files|*.mp3;*.wav;*.aif;*.aiff;*.ogg;*.bwav;*.wma"
                            browser.Title = "Looking for audio file: " & c.ShortFileName
                            browser.Multiselect = False
                            browser.InitialDirectory = FileIO.FileSystem.GetFileInfo(c.FileName).DirectoryName

                            If browser.ShowDialog() = True Then
                                Try
                                    If pack.PartExists(GetAudioUri(c.FileName)) Then
                                        pack.DeletePart(GetAudioUri(c.FileName))
                                    End If
                                    If Not pack.PartExists(GetAudioUri(browser.FileName)) Then
                                        CreateAudioFilePart(pack, browser.FileName)
                                    End If
                                    c.FileName = browser.FileName
                                    UpdatedCount += 1
                                Catch ex As Exception
                                    My.Application.Show.Console.SendMessage("There was a problem locating a missing audio file.", ex)
                                End Try
                            End If
                        Next
                    End If
                End If
            End Using

            If UpdatedCount > 0 Then
                If MsgBox($"{UpdatedCount} {If(UpdatedCount = 1, "file was", "files were")} updated, do you want to save the show now?
                If you don't save the show now, you may end up with unused audio files in your project file.", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
                    My.Application.Show.ShowFile.Save()
                End If
            End If

            Return UpdatedCount

        End Function

        ''' <summary>
        ''' Creates a show file part, copying the show file into it.
        ''' </summary>
        ''' <param name="pack"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Shared Function CreateShowFilePart(pack As System.IO.Packaging.ZipPackage) As System.IO.Packaging.ZipPackagePart
			Dim p As System.IO.Packaging.ZipPackagePart = pack.CreatePart(System.IO.Packaging.PackUriHelper.CreatePartUri(New Uri("ShowFile\" & My.Application.Show.ShowFile.Name, UriKind.Relative)), System.Net.Mime.MediaTypeNames.Application.Octet)
			Dim TempPath As String = My.Application.Show.ShowFile.Path & "\" & My.Application.Show.ShowFile.Name
			If TempPath.EndsWith("x") Then TempPath = TempPath.Remove(TempPath.Length - 1, 1)
			TempPath = TempPath & ".temp"
			CopyStream(New System.IO.StreamReader(TempPath).BaseStream, p.GetStream)
			Return p
		End Function

		''' <summary>
		''' Creates an AudioFilePart and copies the audio file into it.
		''' </summary>
		''' <param name="pack"></param>
		''' <param name="FilePath"></param>
		''' <returns></returns>
		''' <remarks></remarks>
		Private Shared Function CreateAudioFilePart(pack As System.IO.Packaging.ZipPackage, FilePath As String) As System.IO.Packaging.ZipPackagePart
			Dim p As System.IO.Packaging.ZipPackagePart = pack.CreatePart(System.IO.Packaging.PackUriHelper.CreatePartUri(New Uri("AudioFiles\" & FilePath, UriKind.Relative)), System.Net.Mime.MediaTypeNames.Application.Octet)
			CopyStream(New System.IO.StreamReader(FilePath).BaseStream, p.GetStream)
			Return p
		End Function

		''' <summary>
		''' Copy's a stream into another stream.
		''' </summary>
		''' <param name="Source"></param>
		''' <param name="Target"></param>
		''' <remarks></remarks>
		Public Shared Sub CopyStream(Source As System.IO.Stream, Target As System.IO.Stream)
			Dim bufSize As Integer = &H1000
			Dim buf As Byte() = New Byte(bufSize - 1) {}
			Dim bytesRead As Integer = 0
			While (InlineAssignHelper(bytesRead, Source.Read(buf, 0, bufSize))) > 0
				Target.Write(buf, 0, bytesRead)
			End While
			Source.Close()
		End Sub

		''' <summary>
		''' Used by the CopyStream function.
		''' </summary>
		''' <typeparam name="T"></typeparam>
		''' <param name="target"></param>
		''' <param name="value"></param>
		''' <returns></returns>
		''' <remarks></remarks>
		Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
			target = value
			Return value
		End Function

	End Class
End Namespace